package zdream.pmfield.test.stage.effect;

import org.junit.Before;
import org.junit.Test;

import java.util.concurrent.atomic.AtomicBoolean;

import zdream.pmfield.data.foe.Foe;
import zdream.pmfield.data.foe.MoveTemplate;
import zdream.pmfield.data.foe.Type;
import zdream.pmfield.stage.StageBuilderForTest;
import zdream.pmfield.stage.buff.ailment.ParalysisBuff;
import zdream.pmfield.stage.buff.foe.TypeBuff;
import zdream.pmfield.stage.message.RollReportMessage;
import zdream.pmfield.stage.message.FoeAddBuffMessage;
import zdream.pmfield.stage.message.FoeRemoveBuffMessage;
import zdream.pmfield.stage.message.TurnStartMessage;
import zdream.pmfield.storage.FoeTemplateStorage;
import zdream.pmfield.test.utils.ResourceConstant;
import zdream.pmfield.stage.StageWrapper;
import zdream.pmfield.test.utils.StageConstant;

import static org.junit.Assert.*;

/**
 * @author Zdream
 * @date 2023-11-22
 * @since 0.0.1
 */
public class ParalysisBuffCase {
	FoeTemplateStorage storage;

	@Before
	public void before() {
		storage = new FoeTemplateStorage();
		storage.load();
		storage.loadMovesInResource(ResourceConstant.RES_PATH_TEST_MOVES_JSON);
	}

	@Test
	public void testParalysisEffect() {
		MoveTemplate paralysisMove = storage.findMoveById(10000012, 1);
		assertEquals(paralysisMove.name, "肯定麻痹");
		MoveTemplate normalMove = storage.findMoveById(10000010, 1);
		assertEquals(normalMove.name, "肯定成功");

		Foe foe1 = Foe.builder(storage.findFoeById(4))
				.withLevel(20)
				.withMove(paralysisMove)
				.build();
		Foe foe2 = Foe.builder(storage.findFoeById(1))
				.withLevel(20)
				.withMove(normalMove)
				.build();
		final StageWrapper wrapper = StageBuilderForTest.builder()
				.putFoeToOneSide(foe1)
				.putFoeToAnotherSide(foe2)
				.withStorage(storage)
				.buildWrapper();

		System.out.println("TEST 1: apply paralysis");

		AtomicBoolean paralysisRequest = new AtomicBoolean(false);
		AtomicBoolean paralysisApplied = new AtomicBoolean(false);
		AtomicBoolean checked = new AtomicBoolean(false);
		float[] buffAppliedTime = new float[1];

		wrapper.beforeHandleMessage(System.out::println);
		wrapper.onActionRequestForAll(wrapper.createDefaultController().withActionChecker(action -> {
			if (action.move.move == paralysisMove) {
				paralysisRequest.set(true);
			}
		}));
		wrapper.onMessage(FoeAddBuffMessage.class, message -> {
			assertTrue(message.buff.is(StageConstant.EFFECT_PARALYSIS));
			assertTrue(message.success);
			paralysisApplied.set(true);
			buffAppliedTime[0] = wrapper.now();
		});
		wrapper.onMessage(TurnStartMessage.class, message -> {
			if (!paralysisRequest.get()) {
				return;
			}
			assertTrue(paralysisApplied.get());
			checked.set(true);
		});

		wrapper.runUntil(stage0 -> checked.get());
		assertTrue(checked.get());

		System.out.println("TEST 2: paralysis 状态: 使骰子失败");

		wrapper.clearOnMessageHooks();
		checked.set(false);

		wrapper.onDefaultActionRequest();
		wrapper.onMessage(RollReportMessage.class, message -> {
			if (!StageConstant.ROLL_TITLE_ACTION.equals(message.title)) {
				return;
			}
			if (message.foe.foe == foe1) {
				return;
			}

			assertNotNull(message.rolls[0]);
			assertNotNull(message.rolls[0].affectedBy);
			assertTrue(message.rolls[0].affectedBy.is(StageConstant.EFFECT_PARALYSIS));
			assertFalse(message.rolls[0].affectResult);
			assertFalse(message.rolls[0].result);
			checked.set(true);
		});

		wrapper.runUntil(stage0 -> checked.get());
		assertTrue(checked.get());

		System.out.println("TEST 3: paralysis 状态会随时间消失");
		wrapper.clearOnMessageHooks();
		checked.set(false);

		AtomicBoolean paralysisRemoveCheck = new AtomicBoolean(false);

		wrapper.onDefaultActionRequest();
		wrapper.onMessage(RollReportMessage.class, message -> {
			if (message.foe.foe == foe1) {
				return;
			}
			if (StageConstant.EFFECT_PARALYSIS.equals(message.title)) {
				paralysisRemoveCheck.set(true);
				assertTrue(wrapper.now() - buffAppliedTime[0] >= 3);
			} else if (StageConstant.ROLL_TITLE_ACTION.equals(message.title)) {
				assertFalse(message.rolls[0].result);
			}
		});
		wrapper.onMessage(FoeRemoveBuffMessage.class, message -> {
			assertEquals(message.foe.foe, foe2);
			assertTrue(message.buff.is(StageConstant.EFFECT_PARALYSIS));
			checked.set(true);
		});

		wrapper.runUntil(stage0 -> checked.get());
		assertTrue(checked.get());
	}

	@Test
	public void testElectricTypeBlockParalysisBuff() {
		MoveTemplate paralysisMove = storage.findMoveById(10000012, 1);
		assertEquals(paralysisMove.name, "肯定麻痹");
		MoveTemplate normalMove = storage.findMoveById(10000010, 1);
		assertEquals(normalMove.name, "肯定成功");

		Foe foe1 = Foe.builder(storage.findFoeById(4))
				.withLevel(20)
				.withMove(paralysisMove)
				.build();
		Foe foe2 = Foe.builder(storage.findFoeById(25))
				.withLevel(25)
				.withMove(normalMove)
				.build();
		assertEquals(foe2.template.types[0], Type.ELECTRIC);
		StageWrapper wrapper = StageBuilderForTest.builder()
				.putFoeToOneSide(foe1)
				.putFoeToAnotherSide(foe2)
				.withStorage(storage)
				.buildWrapper();

		wrapper.printBeforeHandleMessage();
		AtomicBoolean checked = new AtomicBoolean(false);

		wrapper.onDefaultActionRequest();
		wrapper.onMessage(FoeAddBuffMessage.class, message -> {
			// 麻痹效果对电系无效
			assertTrue(message.buff.is(ParalysisBuff.CATEGORY));
			assertFalse(message.success);
			assertNotNull(message.rejectBy);
			assertTrue(message.rejectBy.is(TypeBuff.CATEGORY));
			checked.set(true);
		});
		wrapper.runUntil(s -> checked.get());

		assertTrue(checked.get());
	}
}
